/*
 * @(#)ConnectionBaseAdapter.java	1.7 06/10/10
 * 
 * Copyright  1990-2008 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation. 
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA 
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 */

package com.sun.cdc.io;

import com.sun.cdc.io.ConnectionBaseInterface;
import com.sun.cdc.io.GeneralBase;

//import com.sun.midp.midlet.*;

//import com.sun.midp.security.*;

import java.io.*;

import javax.microedition.io.*;

/**
 * Protocol classes extend this class to gain some of the common functionality
 * needed to implement a CDC Generic Connection.
 * <p>
 * The common functionality includes:</p>
 * <ul>
 * <li>Supplies the input and output stream classes for a StreamConnection</li>
 * <li>Limits the number of streams opened according to mode, but the limit
 * can be overridden. Read-write allows 1 input and 1 output, write-only
 * allows 1 output, read-only allows 1 input</li>
 * <li>Only "disconnects" when the connection and all streams are closed</li>
 * <li>Throws I/O exceptions when used after being closed</li>
 * <li>Provides a more efficient implementation of
 * {@link InputStream#read(byte[], int, int)}, which is called by
 * {@link InputStream#read()}
 * <li>Provides a more efficient implementation of
 * {@link OutputStream#write(byte[], int, int)}, which is called by
 * {@link OutputStream#write(int)}
 * </ul>
 * <p align="center">
 * <b>Class Relationship Diagram</b></p>
 * <p align="center">
 * <img src="doc-files/ConnectionBaseAdapter.gif" border=0></p>
 *
 * @version 3.0 9/1/2000
 */
public abstract class ConnectionBaseAdapter implements ConnectionBaseInterface,
    StreamConnection {

    /** Flag indicating if the connection is open. */
    protected boolean connectionOpen = false;
    /** Number of input streams that were opened. */
    protected static int iStreams = 0;
    /**
     * Maximum number of open input streams. Set this
     * to zero to prevent openInputStream from giving out a stream in
     * write-only mode.
     */
    protected int maxIStreams = 1;
    /** Number of output streams were opened. */
    protected static int oStreams = 0;
    /**
     * Maximum number of output streams. Set this
     * to zero to prevent openOutputStream from giving out a stream in
     * read-only mode.
     */
    protected int maxOStreams = 1;

    /**
     * Check for required permission and open a connection to a target.
     *
     * @param name             URL for the connection, without the
     *                         without the protocol part
     * @param mode             I/O access mode, see {@link Connector}
     * @param timeouts         flag to indicate that the caller
     *                         wants timeout exceptions
     * @return                 this Connection object
     *
     * @exception IllegalArgumentException If a parameter is invalid.
     * @exception ConnectionNotFoundException If the connection cannot
     *                                        be found.
     * @exception IOException  If some other kind of I/O error occurs.
     */
    public Connection openPrim(String name, int mode, boolean timeouts)
            throws IOException {
	///        checkForPermission(null, name);	// Give the subclass a chance to check
	///	checkForPermission(name);	// Give the subclass a chance to check

        switch (mode) {
        case Connector.READ:
        case Connector.WRITE:
        case Connector.READ_WRITE:
            break;

        default:
            throw new IllegalArgumentException("Illegal mode");
        }

        connect(name, mode, timeouts);
        connectionOpen = true;
        return this;
    }


    /**
     * Overridden by Protocols to check for permissions.
     * This implementation always throws a security exception.
     * The subclass is responsible for checking permissions and
     * maintaining the state (in private local fields) as to whether
     * it was granted. 
     *
     * @param token security token of the calling class or null
     * @param name the URL of the connection without the protocol
     *
     * @exception SecurityException if permissions are not granted
     * @exception InterruptedIOException if I/O associated with permissions is interrupted
     */
    ///    protected void checkForPermission(SecurityToken token, String name)
    protected void checkForPermission()
	throws SecurityException, InterruptedIOException
    {
	throw new SecurityException("Permission not granted");
    }

    /**
     * Utility method to check for the required permission, and handle
     * prompts, etc.  A SecurityToken may be supplied, in which case
     * the permission must be allowed by the token. If the token is
     * <code>null</code> then the permission is checked in the current
     * app.  If there is no app then the permission
     * is allowed.  A SecurityException is thrown when the permission
     * is not granted.
     *
     * @param token security token of the calling class or null
     * @param requiredPermission the permission that is needed
     * @param name resource to insert into the permission question
     * @param protocol the protocol string used in the resource name
     *
     * @exception SecurityException if the permission is not granted
     * @exception InterruptedIOException if another thread interrupts the
     *   calling thread while this method is waiting to preempt the
     *   display.
     */
    ///    protected final void checkForPermission(SecurityToken token, 
	    ///					    int requiredPermission,
	    ///			    String name, String protocol)
	    ///	    protected final void checkForPermission()
	    ///	throws InterruptedIOException
    ///{
	/// If a security token was supplied, use it for the check
	///	if (token != null) {
	///    token.checkIfPermissionAllowed(requiredPermission);
	///    return;
	///}
	
	///        Scheduler scheduler;
	///        MIDletSuite midletSuite;

	///        scheduler = Scheduler.getScheduler();
	///        midletSuite = scheduler.getMIDletSuite();

	    /// there is no suite running when installing from the command line
	    ///        if (midletSuite != null) {
	    ///    if (protocol != null) {
	    ///        name = protocol + ":" + name;
	    ///    }

	    ///    try {
	    ///        midletSuite.checkForPermission(requiredPermission, name);
	    ///    } catch (InterruptedException ie) {
	    ///        throw new InterruptedIOException(
	    ///            "Interrupted while trying to ask the user permission");
	    ///    }
	    ///}
    ///}

    /**
     * Check for the required permission and open a connection to a target.
     * This method can be used with permissions greater than
     * the current app.
     * 
     * @param token            security token of the calling class
     * @param name             URL for the connection, without the
     *                         without the protocol part
     * @param mode             I/O access mode, see {@link Connector}
     * @param timeouts         flag to indicate that the caller
     *                         wants timeout exceptions
     * @return                 this Connection object
     *
     * @exception IllegalArgumentException If a parameter is invalid.
     * @exception ConnectionNotFoundException If the connection cannot
     *                                        be found.
     * @exception IOException  If some other kind of I/O error occurs.
     */
///    public Connection openPrim(SecurityToken token, String name, int mode,
    ///                               boolean timeouts) throws IOException {
    ///        checkForPermission(token, name);
	///    public Connection openPrim(String name, int mode,
	///			       boolean timeouts) throws IOException {
	///            checkForPermission();

	///        return openPrim(name, mode, timeouts);
	///    }

    /**
     * Check for required permission and open a connection to a target.
     * This method can be used with permissions greater than
     * the current app. Assume read/write and no timeouts.
     * 
     * @param token            security token of the calling class
     * @param name             URL for the connection, without the
     *                         without the protocol part
     * @return                 this Connection object
     *
     * @exception IllegalArgumentException If a parameter is invalid.
     * @exception ConnectionNotFoundException If the connection cannot
     *                                        be found.
     * @exception IOException  If some other kind of I/O error occurs.
     */
///    public Connection openPrim(SecurityToken token, String name)
	///            throws IOException {
	///        return openPrim(token, name, Connector.READ_WRITE, false);
    public Connection openPrim(String name)
            throws IOException {
        return openPrim(name, Connector.READ_WRITE, false);
    }

    /**
     * Returns an input stream.
     *
     * @return     an input stream for writing bytes to this port.
     * @exception  IOException  if an I/O error occurs when creating the
     *                          output stream.
     */
    public InputStream openInputStream() throws IOException {
        InputStream i;

        ensureOpen();

        /* Fix for CR 6246819: Comment out MIDP code that limits streams so
           that multiple streams are supported for CDC */ 
        /*if (maxIStreams == 0) {
          throw new IOException("no more input streams available");
          }*/
        
        i = new BaseInputStream(this);
        //maxIStreams--;
        iStreams++;
        return i;
    }

    /**
     * Open and return a data input stream for a connection.
     *
     * @return                 An input stream
     * @exception IOException  If an I/O error occurs
     */
    public DataInputStream openDataInputStream() throws IOException {
        return new DataInputStream(openInputStream());
    }

    /**
     * Returns an output stream.
     *
     * @return     an output stream for writing bytes to this port.
     * @exception  IOException  if an I/O error occurs when creating the
     *                          output stream.
     */
    public OutputStream openOutputStream() throws IOException {
        OutputStream o;

        ensureOpen();
        /* Fix for CR 6246819: Comment out MIDP code that limits streams so
           that multiple streams are supported for CDC */ 
        /*if (maxOStreams == 0) {
          throw new IOException("no more output streams available");
          }*/

        o = new BaseOutputStream(this);
        //maxOStreams--;
        oStreams++;
        return o;
    }

    /**
     * Open and return a data output stream for a connection.
     *
     * @return                 An input stream
     * @exception IOException  If an I/O error occurs
     */
    public DataOutputStream openDataOutputStream() throws IOException {
        return new DataOutputStream(openOutputStream());
    }

    /**
     * Close the connection.
     *
     * @exception  IOException  if an I/O error occurs when closing the
     *                          connection.
     */
    public void close() throws IOException {
        if (connectionOpen) {
            connectionOpen = false;
            closeCommon();
        }
    }

    /**
     * Called once by each child input stream.
     * If the input stream is marked open, it will be marked closed and
     * the if the connection and output stream are closed the disconnect
     * method will be called.
     *
     * @exception IOException if the subclass throws one
     */
    protected void closeInputStream() throws IOException {
        if (iStreams>0) {
            iStreams--;
            closeCommon();
        }
    }

    /**
     * Called once by each child output stream.
     * If the output stream is marked open, it will be marked closed and
     * the if the connection and input stream are closed the disconnect
     * method will be called.
     *
     * @exception IOException if the subclass throws one
     */
    protected void closeOutputStream() throws IOException {
        if (oStreams>0) {
            oStreams--;
            closeCommon();
        }
    }

    /**
     * Disconnect if the connection and all the streams and the closed.
     *
     * @exception  IOException  if an I/O error occurs when closing the
     *                          connection.
     */
    void closeCommon() throws IOException {
        if (!connectionOpen && iStreams == 0 && oStreams == 0) {
            disconnect();
        }
    }

    /**
     * Check if the connection is open.
     *
     * @exception  IOException  is thrown, if the stream is not open.
     */
    protected void ensureOpen() throws IOException {
        if (!connectionOpen) {
            throw new IOException("Connection closed");
        }
    }

     /**
     * Check if the streams are open.
     *
     * @exception  IOException  is thrown, if the stream is still open.
     */
    protected void ensureNoStreamsOpen() throws IOException {
        if ((iStreams > 0) || (oStreams > 0)) {
            throw new IOException("Stream is still open");
        }
    }

   /**
     * Connect to a target.
     *
     * @param name             URL for the connection, without the protocol
     *                         part
     * @param mode             I/O access mode, see {@link Connector}
     * @param timeouts         flag to indicate that the called wants
     *                         timeout exceptions
     *
     * @exception IllegalArgumentException If a parameter is invalid.
     * @exception ConnectionNotFoundException If the connection cannot be
     *             found.
     * @exception IOException  If some other kind of I/O error occurs.
     */
    protected abstract void connect(String name, int mode, boolean timeouts)
        throws IOException;

    /**
     * Free up the connection resources.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    protected abstract void disconnect() throws IOException;

    /**
     * Reads up to <code>len</code> bytes of data from the input stream into
     * an array of bytes, blocks until at least one byte is available.
     *
     * @param      b     the buffer into which the data is read.
     * @param      off   the start offset in array <code>b</code>
     *                   at which the data is written.
     * @param      len   the maximum number of bytes to read.
     * @return     the total number of bytes read into the buffer, or
     *             <code>-1</code> if there is no more data because the end of
     *             the stream has been reached.
     * @exception  IOException  if an I/O error occurs.
     */
    protected abstract int readBytes(byte b[], int off, int len)
        throws IOException;

    /**
     * Returns the number of bytes that can be read (or skipped over) from
     * this input stream without blocking by the next caller of a method for
     * this input stream.  The next caller might be the same thread or
     * another thread. This classes implementation always returns
     * <code>0</code>. It is up to subclasses to override this method.
     *
     * @return     the number of bytes that can be read from this input stream
     *             without blocking.
     * @exception  IOException  if an I/O error occurs.
     */
    public int available() throws IOException {
        return 0;
    }

    /**
     * Writes <code>len</code> bytes from the specified byte array
     * starting at offset <code>off</code> to this output stream.
     * <p>
     * Polling the native code is done here to allow for simple
     * asynchronous native code to be written. Not all implementations
     * work this way (they block in the native code) but the same
     * Java code works for both.
     *
     * @param      b     the data.
     * @param      off   the start offset in the data.
     * @param      len   the number of bytes to write.
     * @return     number of bytes written
     * @exception  IOException  if an I/O error occurs. In particular,
     *             an <code>IOException</code> is thrown if the output
     *             stream is closed.
     */
    protected abstract int writeBytes(byte b[], int off, int len)
        throws IOException;

    /**
     * Forces any buffered output bytes to be written out.
     * The general contract of <code>flush</code> is
     * that calling it is an indication that, if any bytes previously
     * written that have been buffered by the connection,
     * should immediately be written to their intended destination.
     * <p>
     * The <code>flush</code> method of <code>ConnectionBaseAdapter</code>
     * does nothing.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    protected void flush() throws IOException {
    }

    /**
     * Tests if input stream for a connection supports the <code>mark</code> and
     * <code>reset</code> methods.
     *
     * <p> The <code>markSupported</code> method of
     * <code>ConnectionBaseAdapter</code> returns <code>false</code>.
     *
     * <p> Subclasses should override this method if they support own mark/reset
     * functionality.
     *
     * @return  <code>true</code> if input stream for this connection supports
     *           the <code>mark</code> and <code>reset</code> methods;
     *           <code>false</code> otherwise.
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.InputStream#reset()
     */
    public boolean markSupported() {
        return false;
    }

    /**
     * Marks the current position in input stream for a connection.
     * A subsequent call to the <code>reset</code> method repositions this
     * stream at the last marked position so that subsequent reads re-read
     * the same bytes.
     *
     * <p> The <code>mark</code> method of <code>ConnectionBaseAdapter</code>
     *  does nothing.
     *
     * <p> Subclasses should override this method if they support own mark/reset
     * functionality.
     *
     * @param   readlimit   the maximum limit of bytes that can be read before
     *                      the mark position becomes invalid.
     * @see     java.io.InputStream#reset()
     */
    public synchronized void mark(int readlimit) {}

    /**
     * Repositions input stream for a connection to the position at the time the
     * <code>mark</code> method was last called on this input stream.
     *
     * <p> The method <code>reset</code> for <code>ConnectionBaseAdapter</code>
     * class does nothing and always throws an <code>IOException</code>.
     *
     * <p> Subclasses should override this method if they support own mark/reset
     * functionality.
     *
     * @exception  IOException  if this stream has not been marked or if the
     *                          mark has been invalidated.
     * @see     java.io.InputStream#reset()
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.IOException
     */
    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }
}

/**
 * Input stream for the connection
 */
class BaseInputStream extends InputStream {

    /** Pointer to the connection */
    private ConnectionBaseAdapter parent;

    /** Buffer for single char reads */
    byte[] buf = new byte[1];

    /**
      * Buffer for mark/reset funtionality support.
      * <code>null</code> value indicates <code>mark</code> was not called or
      * <code>readlimit</code> value of the last <code>mark</code> was exceeded.
      */
    byte[] markBuf = null;

    /** The size of data stored in <code>markBuf</code>. */
    int markSize = 0;

    /** Current position in <code>markBuf</code> to read data from. */
    int markPos = 0;

    /**
     * Indicates whether <code>reset</code> method was called.
     * If so, data is read from <code>markBuf</code> buffer,
     * otherwise via <code>parent.readBytes</code> method.
     */
    boolean isReadFromBuffer = false;

    /**
     * Constructs a BaseInputStream for a ConnectionBaseAdapter.
     *
     * @param parent pointer to the connection object
     *
     * @exception  IOException  if an I/O error occurs.
     */
    BaseInputStream(ConnectionBaseAdapter parent) throws IOException {
        this.parent = parent;
    }

    /**
     * Check the stream is open
     *
     * @exception  InterruptedIOException  if it is not.
     */
    private void ensureOpen() throws InterruptedIOException {
        if (parent == null) {
            throw new InterruptedIOException("Stream closed");
        }
    }

    /**
     * Returns the number of bytes that can be read (or skipped over) from
     * this input stream without blocking by the next caller of a method for
     * this input stream.  The next caller might be the same thread or
     * another thread.
     *
     * <p>The <code>available</code> method always returns <code>0</code> if
     * {@link ConnectionBaseAdapter#available()} is
     * not overridden by the subclass.
     *
     * @return     the number of bytes that can be read from this input stream
     *             without blocking.
     * @exception  IOException  if an I/O error occurs.
     */
    public int available() throws IOException {

        ensureOpen();

        return parent.available();
    }

    /**
     * Reads the next byte of data from the input stream. The value byte is
     * returned as an <code>int</code> in the range <code>0</code> to
     * <code>255</code>. If no byte is available because the end of the stream
     * has been reached, the value <code>-1</code> is returned. This method
     * blocks until input data is available, the end of the stream is detected,
     * or an exception is thrown.
     *
     * @return     the next byte of data, or <code>-1</code> if the end of the
     *             stream is reached.
     * @exception  IOException  if an I/O error occurs.
     */
    public int read() throws IOException {
        if (read(buf, 0, 1) > 0) {
            return (buf[0] & 0xFF);
        }

        return -1;
    }

    /**
     * Reads up to <code>len</code> bytes of data from the input stream into
     * an array of bytes.  An attempt is made to read as many as
     * <code>len</code> bytes, but a smaller number may be read, possibly
     * zero. The number of bytes actually read is returned as an integer.
     *
     * <p> This method blocks until input data is available, end of file is
     * detected, or an exception is thrown.
     *
     * <p> If <code>b</code> is <code>null</code>, a
     * <code>NullPointerException</code> is thrown.
     *
     * <p> If <code>off</code> is negative, or <code>len</code> is negative, or
     * <code>off+len</code> is greater than the length of the array
     * <code>b</code>, then an <code>IndexOutOfBoundsException</code> is
     * thrown.
     *
     * <p> If <code>len</code> is zero, then no bytes are read and
     * <code>0</code> is returned; otherwise, there is an attempt to read at
     * least one byte. If no byte is available because the stream is at end of
     * file, the value <code>-1</code> is returned; otherwise, at least one
     * byte is read and stored into <code>b</code>.
     *
     * <p> The first byte read is stored into element <code>b[off]</code>, the
     * next one into <code>b[off+1]</code>, and so on. The number of bytes read
     * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
     * bytes actually read; these bytes will be stored in elements
     * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
     * leaving elements <code>b[off+</code><i>k</i><code>]</code> through
     * <code>b[off+len-1]</code> unaffected.
     *
     * <p> In every case, elements <code>b[0]</code> through
     * <code>b[off]</code> and elements <code>b[off+len]</code> through
     * <code>b[b.length-1]</code> are unaffected.
     *
     * <p> If the first byte cannot be read for any reason other than end of
     * file, then an <code>IOException</code> is thrown. In particular, an
     * <code>IOException</code> is thrown if the input stream has been closed.
     *
     * @param      b     the buffer into which the data is read.
     * @param      off   the start offset in array <code>b</code>
     *                   at which the data is written.
     * @param      len   the maximum number of bytes to read.
     * @return     the total number of bytes read into the buffer, or
     *             <code>-1</code> if there is no more data because the end of
     *             the stream has been reached.
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.InputStream#read()
     */
    public int read(byte b[], int off, int len) throws IOException {
        int test;

        ensureOpen();

        if (len == 0) {
            return 0;
        }

        /*
         * test the parameters so the subclass will not have to.
         * this will avoid crashes in the native code
         */
        test = b[off] + b[len - 1] + b[off + len - 1];

        // use parent's mark/reset functionality
        // if the parent supports the own one
        if (parent.markSupported()) {
            return parent.readBytes(b, off, len);
        }

        // read data from mark buffer if reset method was called
        if (isReadFromBuffer) {
            int dataSize = markSize - markPos;
            if (dataSize > 0) {
                int copySize = (dataSize > len) ? len : dataSize;
                System.arraycopy(markBuf, markPos, b, off, copySize);
                markPos += copySize;

                // read data directly from the stream
                // if size of data in the buffer is not enough
                int readSize = 0;
                if (copySize < len) {
                    readSize = parent.readBytes(
                        b, off + copySize, len - copySize);

                    // check if eos is reached
                    if (readSize == -1) {
                        readSize = 0;
                    } else {
                        // check the mark buffer overflow
                        if (markSize + readSize > markBuf.length) {
                            markBuf = null;
                        // cache the data in the mark buffer
                        } else {
                            System.arraycopy(
                                b, off + copySize, markBuf, markSize, readSize);
                            markSize += readSize;
                        }
                    }

                    isReadFromBuffer = false;
                }

                return copySize + readSize;
            } else {
                isReadFromBuffer = false;
            }
        }

        int readSize = parent.readBytes(b, off, len);

        // fill mark buffer if exists
        if (markBuf != null) {
            if (readSize > 0) {
                // check the mark buffer overflow
                if (markSize + readSize > markBuf.length) {
                    markBuf = null;
                    // cache the data in the mark buffer
                } else {
                    System.arraycopy(b, off, markBuf, markSize, readSize);
                    markSize += readSize;
                }
            }
        }

        return readSize;
    }

    /**
     * Closes this input stream and releases any system resources associated
     * with the stream.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public void close() throws IOException {
        if (parent != null) {
            parent.closeInputStream();
            parent = null;
        }
	// free buffer used by mark/reset operations if it was allocated
	markBuf = null;
    }

    /**
     * Tests if this input stream supports the <code>mark</code> and
     * <code>reset</code> methods.
     *
     * <p>The <code>markSupported</code> method of
     * <code>BaseInputStream</code> returns <code>true</code>.
     *
     * @return  always <code>true</code>
     *
     * @see     BaseInputStream#mark(int)
     * @see     BaseInputStream#reset()
     * @see     java.io.InputStream#markSupported()
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.InputStream#reset()
     */
    public boolean markSupported() {
        return true;
    }

    /**
     * Marks the current position in this input stream. A subsequent call to
     * the <code>reset</code> method repositions this stream at the last marked
     * position so that subsequent reads re-read the same bytes.
     *
     * <p> The <code>readlimit</code> arguments tells this input stream to
     * allow that many bytes to be read before the mark position gets
     * invalidated.
     *
     * <p> The stream remembers all the bytes read after the call to
     * <code>mark</code> and stands ready to supply those same bytes again
     * if and whenever the method <code>reset</code> is called.
     *  However, the stream is not required to remember any data at all if more
     * than <code>readlimit</code> bytes are read from the stream before
     * <code>reset</code> is called.
     *
     * @param   readlimit   the maximum limit of bytes that can be read before
     *                      the mark position becomes invalid.
     * @see     BaseInputStream#reset()
     * @see     java.io.InputStream#reset()
     * @see     java.io.InputStream#mark(int)
     */
    public synchronized void mark(int readlimit) {
        // check whether the stream is closed
        if (parent == null) {
            return;
        }

        // use parent's mark/reset functionality
        // if the parent supports the own one
        if (parent.markSupported()) {
            parent.mark(readlimit);
        } else {
            byte[] oldBuf = markBuf;

            // copy relevant data from old buffer if any
            if (isReadFromBuffer) {
                int oldDataSize = markSize - markPos;
                if (readlimit < oldDataSize) {
                    readlimit = oldDataSize;
                }
                markBuf = new byte[readlimit];
                System.arraycopy(oldBuf, markPos, markBuf, 0, oldDataSize);
                markSize = oldDataSize;
            } else {
                markBuf = new byte[readlimit];
                markSize = 0;
            }
            markPos = 0;
        }
    }

    /**
     * Repositions this stream to the position at the time the
     * <code>mark</code> method was last called on this input stream.
     *
     * <p> If the method <code>mark</code> has not been called since
     * the stream was created, or the number of bytes read from the stream
     * since <code>mark</code> was last called is larger than the argument
     * to <code>mark</code> at that last call, then an
     * <code>IOException</code> is thrown.
     *
     * <p> If such an <code>IOException</code> is not thrown, then the
     * stream is reset to a state such that all the bytes read since the
     * most recent call to <code>mark</code> will be resupplied
     * to subsequent callers of the <code>read</code> method, followed by
     * any bytes that otherwise would have been the next input data as of
     * the time of the call to <code>reset</code>.
     *
     * @exception  IOException  if this stream has not been marked or if the
     *                          mark has been invalidated;
     *                          or if the stream is closed
     * @see     BaseInputStream#mark(int)
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.InputStream#reset(int)
     * @see     java.io.IOException
     */
    public synchronized void reset() throws IOException {
        ensureOpen();

        // use parent's mark/reset functionality
        // if the parent supports the own one
        if (parent.markSupported()) {
            parent.reset();
        } else {
            if (markBuf == null) {
                throw new IOException("Invalid mark position");
            }
            markPos = 0;
            isReadFromBuffer = true;
        }
    }
}


/**
 * Output stream for the connection
 */
class BaseOutputStream extends OutputStream {

    /** Pointer to the connection */
    ConnectionBaseAdapter parent;

    /** Buffer for single char writes */
    byte[] buf = new byte[1];

    /**
     * Constructs a BaseOutputStream for an ConnectionBaseAdapter.
     *
     * @param p parent connection
     */
    BaseOutputStream(ConnectionBaseAdapter p) {
        parent = p;
    }

    /**
     * Check the stream is open
     *
     * @exception  InterruptedIOException  if it is not.
     */
    private void ensureOpen() throws InterruptedIOException {
        if (parent == null) {
            throw new InterruptedIOException("Stream closed");
        }
    }

    /**
     * Writes the specified byte to this output stream. The general
     * contract for <code>write</code> is that one byte is written
     * to the output stream. The byte to be written is the eight
     * low-order bits of the argument <code>b</code>. The 24
     * high-order bits of <code>b</code> are ignored.
     *
     * @param      b   the <code>byte</code>.
     * @exception  IOException  if an I/O error occurs. In particular,
     *             an <code>IOException</code> may be thrown if the
     *             output stream has been closed.
     */
    public void write(int b) throws IOException {
        buf[0] = (byte)b;
        write(buf, 0, 1);
    }

    /**
     * Writes <code>len</code> bytes from the specified byte array
     * starting at offset <code>off</code> to this output stream.
     * The general contract for <code>write(b, off, len)</code> is that
     * some of the bytes in the array <code>b</code> are written to the
     * output stream in order; element <code>b[off]</code> is the first
     * byte written and <code>b[off+len-1]</code> is the last byte written
     * by this operation.
     * <p>
     * If <code>b</code> is <code>null</code>, a
     * <code>NullPointerException</code> is thrown.
     * <p>
     * If <code>off</code> is negative, or <code>len</code> is negative, or
     * <code>off+len</code> is greater than the length of the array
     * <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown.
     *
     * @param      b     the data.
     * @param      off   the start offset in the data.
     * @param      len   the number of bytes to write.
     * @exception  IOException  if an I/O error occurs. In particular,
     *             an <code>IOException</code> is thrown if the output
     *             stream is closed.
     */
    public void write(byte b[], int off, int len)
           throws IOException {
        int test;
        int bytesWritten;

        ensureOpen();

        if (len == 0) {
            return;
        }

        /*
         * test the parameters here so subclases do not have to,
         * this will avoid a crash in the native code
         */
        test = b[off] + b[len - 1] + b[off + len - 1];

        /*
         * Polling the native code is done here to allow for simple
         * asynchronous native code to be written. Not all implementations
         * work this way (they block in the native code) but the same
         * Java code works for both.
         */
        for (bytesWritten = 0; ; ) {
            try {
                bytesWritten += parent.writeBytes(b, off + bytesWritten,
                                                  len - bytesWritten);
            } finally {
                if (parent == null) {
                    throw new InterruptedIOException("Stream closed");
                }
            }

            if (bytesWritten == len) {
                break;
            }
            
///            GeneralBase.iowait(); 
        }
    }

    /**
     * Flushes this output stream and forces any buffered output bytes
     * to be written out. The general contract of <code>flush</code> is
     * that calling it is an indication that, if any bytes previously
     * written have been buffered by the implementation of the output
     * stream, such bytes should immediately be written to their
     * intended destination.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public void flush() throws IOException {
        ensureOpen();
        parent.flush();
    }

    /**
     * Closes this output stream and releases any system resources
     * associated with this stream. The general contract of <code>close</code>
     * is that it closes the output stream. A closed stream cannot perform
     * output operations and cannot be reopened.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public void close() throws IOException {
        if (parent != null) {
            parent.closeOutputStream();
            parent = null;
        }
    }
}
        
